<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>专家视频标注工具 - 康复评估系统</title>
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }
        body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f5f5f5; padding: 20px; }
        .container { max-width: 1400px; margin: 0 auto; background: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 10px 10px 0 0; }
        .header h1 { margin-bottom: 10px; }
        .section { padding: 30px; border-bottom: 1px solid #eee; }
        .upload-area { border: 2px dashed #ccc; border-radius: 8px; padding: 40px; text-align: center; transition: all 0.3s; }
        .upload-area:hover { border-color: #667eea; background: #f9f9ff; }
        .upload-area.dragover { border-color: #667eea; background: #e9e9ff; }
        .btn { padding: 12px 24px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; transition: all 0.3s; }
        .btn-primary { background: #667eea; color: white; }
        .btn-primary:hover { background: #5568d3; }
        .btn-success { background: #10b981; color: white; }
        .btn-success:hover { background: #059669; }
        .btn-danger { background: #ef4444; color: white; }
        .btn-danger:hover { background: #dc2626; }
        .btn:disabled { opacity: 0.5; cursor: not-allowed; }
        #videoPreview { width: 100%; max-width: 800px; margin: 20px auto; display: block; border-radius: 8px; }
        .timeline { position: relative; height: 80px; background: #f0f0f0; border-radius: 8px; margin: 20px 0; overflow: hidden; }
        .segment { position: absolute; height: 100%; border: 2px solid #667eea; background: rgba(102, 126, 234, 0.2); cursor: move; transition: all 0.2s; }
        .segment:hover { background: rgba(102, 126, 234, 0.3); }
        .segment.labeled { background: rgba(16, 185, 129, 0.3); border-color: #10b981; }
        .segment-label { position: absolute; top: 5px; left: 5px; background: white; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; }
        .segment-time { position: absolute; bottom: 5px; left: 5px; background: rgba(0,0,0,0.7); color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px; }
        .resize-handle { position: absolute; width: 10px; height: 100%; top: 0; cursor: ew-resize; background: rgba(0,0,0,0.1); }
        .resize-handle.left { left: 0; }
        .resize-handle.right { right: 0; }
        .resize-handle:hover { background: rgba(0,0,0,0.3); }
        .segments-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; margin-top: 20px; }
        .segment-card { background: #f9f9f9; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; }
        .segment-card.labeled { border-left-color: #10b981; }
        .segment-card h3 { margin-bottom: 10px; color: #333; }
        .segment-card label { display: block; margin: 10px 0; font-weight: bold; color: #666; }
        .segment-card select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; margin-top: 5px; }
        .segment-card .info { color: #888; font-size: 13px; margin-top: 10px; }
        .controls { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
        .status { padding: 10px 20px; background: #e9e9ff; border-radius: 6px; color: #667eea; font-weight: bold; margin: 20px 0; }
        .status.success { background: #d1fae5; color: #059669; }
        .status.error { background: #fee2e2; color: #dc2626; }
        .hidden { display: none; }
        input[type="file"] {
            position: absolute;
            width: 1px;
            height: 1px;
            padding: 0;
            margin: -1px;
            overflow: hidden;
            clip: rect(0,0,0,0);
            white-space: nowrap;
            border: 0;
        }
        .progress { height: 4px; background: #667eea; transition: width 0.3s; border-radius: 2px; }

        /* 帧预览窗口 - 已禁用 */
        .frame-preview {
            display: none !important;
            position: fixed;
            width: 240px;
            height: 180px;
            border: 3px solid #667eea;
            border-radius: 8px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.3);
            background: white;
            pointer-events: none;
            z-index: 1000;
            overflow: hidden;
            transition: opacity 0.2s;
        }
        .frame-preview img {
            width: 100%;
            height: 100%;
            object-fit: contain;
            display: block;
        }
        .frame-preview img.hidden {
            display: none !important;
        }
        .frame-preview-loading {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100%;
            color: #667eea;
            font-size: 14px;
        }
        .frame-preview-loading.hidden {
            display: none !important;
        }
        .frame-preview-info {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 4px 8px;
            font-size: 11px;
            text-align: center;
        }
        .frame-preview.error {
            border-color: #ef4444;
        }
        .frame-preview.error .frame-preview-loading {
            color: #ef4444;
        }

        /* 患者视频评估相关样式 */
        .divider {
            height: 40px;
            background: linear-gradient(to bottom, #f5f5f5 0%, #667eea 50%, #f5f5f5 100%);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            color: white;
            font-weight: bold;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
        .patient-video-preview {
            width: 100%;
            max-width: 800px;
            margin: 20px auto;
            display: block;
            border-radius: 8px;
            border: 2px solid #667eea;
        }
        .results-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
            gap: 20px;
            margin-top: 20px;
        }
        .result-card {
            background: white;
            border: 2px solid #e5e7eb;
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            transition: all 0.3s;
        }
        .result-card:hover {
            box-shadow: 0 4px 16px rgba(0,0,0,0.15);
            transform: translateY(-2px);
        }
        .result-card.excellent { border-color: #10b981; }
        .result-card.good { border-color: #3b82f6; }
        .result-card.fair { border-color: #f59e0b; }
        .result-card.poor { border-color: #ef4444; }
        .result-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid #e5e7eb;
        }
        .result-header h3 {
            color: #1f2937;
            font-size: 18px;
        }
        .score-badge {
            padding: 6px 16px;
            border-radius: 20px;
            font-size: 18px;
            font-weight: bold;
            color: white;
        }
        .score-badge.excellent { background: #10b981; }
        .score-badge.good { background: #3b82f6; }
        .score-badge.fair { background: #f59e0b; }
        .score-badge.poor { background: #ef4444; }
        .result-details {
            color: #6b7280;
            font-size: 14px;
            line-height: 1.8;
        }
        .result-details div {
            display: flex;
            justify-content: space-between;
            padding: 5px 0;
        }
        .result-details strong {
            color: #374151;
        }
        .metric-bar {
            height: 8px;
            background: #e5e7eb;
            border-radius: 4px;
            overflow: hidden;
            margin: 5px 0;
        }
        .metric-bar-fill {
            height: 100%;
            transition: width 0.5s ease;
        }
        .metric-bar-fill.excellent { background: #10b981; }
        .metric-bar-fill.good { background: #3b82f6; }
        .metric-bar-fill.fair { background: #f59e0b; }
        .metric-bar-fill.poor { background: #ef4444; }
        .overall-summary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            border-radius: 10px;
            margin-bottom: 20px;
            text-align: center;
        }
        .overall-summary h2 {
            font-size: 24px;
            margin-bottom: 10px;
        }
        .overall-summary .big-score {
            font-size: 72px;
            font-weight: bold;
            margin: 20px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🎯 专家视频标注工具</h1>
            <p>上传专家演示视频 → 自动分割 → 标注动作类型 → 生成模板</p>
        </div>

        <!-- 步骤1: 上传视频 -->
        <div class="section" id="uploadSection">
            <h2>步骤 1: 上传专家视频</h2>
            <div class="upload-area" id="uploadArea">
                <p style="font-size:48px; margin-bottom:10px;">📁</p>
                <p style="font-size:18px; margin-bottom:20px;">拖拽视频文件到此处，或点击选择文件</p>
                <label for="videoFile" class="btn btn-primary">选择视频文件</label>
                <input type="file" id="videoFile" accept="video/*">
                <p style="margin-top:15px; color:#888; font-size:14px;">支持 MP4, AVI, MOV 等格式</p>
            </div>
            <div id="uploadStatus" class="status hidden"></div>
        </div>

        <!-- 步骤2: 视频预览和自动分割 -->
        <div class="section hidden" id="previewSection">
            <h2>步骤 2: 视频预览与自动分割</h2>
            <video id="videoPreview" controls></video>
            <div class="controls">
                <button class="btn btn-primary" id="segmentBtn">🔪 自动分割视频</button>
                <span id="videoInfo" style="color:#888;"></span>
            </div>
            <div id="segmentStatus" class="status hidden"></div>
        </div>

        <!-- 步骤3: 时间轴可视化 -->
        <div class="section hidden" id="timelineSection">
            <h2>步骤 3: 分段时间轴（可拖拽调整）</h2>
            <div class="timeline" id="timeline"></div>
            <p style="color:#888; margin-top:10px;">
                💡 提示: <strong>鼠标悬停在分段边界</strong>（左右两侧的深色区域），<strong>点击并拖动</strong>可调整起止时间
            </p>
        </div>

        <!-- 步骤4: 标注分段 -->
        <div class="section hidden" id="labelSection">
            <h2>步骤 4: 标注分段动作类型</h2>
            <div class="segments-list" id="segmentsList"></div>
            <div class="controls" style="margin-top:30px;">
                <button class="btn btn-success" id="saveBtn">💾 保存标注并生成模板</button>
                <button class="btn btn-danger" id="resetBtn">🔄 重新开始</button>
            </div>
            <div id="saveStatus" class="status hidden"></div>
        </div>
    </div>

    <!-- 分隔线 -->
    <div class="divider">
        ⬇️ 患者视频评估 ⬇️
    </div>

    <!-- 患者视频评估容器 -->
    <div class="container" style="margin-top: 20px;">
        <div class="header">
            <h1>🏥 患者视频评估</h1>
            <p>上传患者运动视频 → 自动分割与评分 → 查看详细结果</p>
        </div>

        <!-- 步骤5: 上传患者视频 -->
        <div class="section" id="patientUploadSection">
            <h2>步骤 5: 上传患者视频</h2>
            <div class="upload-area" id="patientUploadArea">
                <p style="font-size:48px; margin-bottom:10px;">🎥</p>
                <p style="font-size:18px; margin-bottom:20px;">拖拽患者视频文件到此处，或点击选择文件</p>
                <label for="patientVideoFile" class="btn btn-primary">选择患者视频</label>
                <input type="file" id="patientVideoFile" accept="video/*">
                <p style="margin-top:15px; color:#888; font-size:14px;">支持 MP4, AVI, MOV 等格式</p>
            </div>
            <div id="patientUploadStatus" class="status hidden"></div>
        </div>

        <!-- 步骤6: 患者视频预览和评估 -->
        <div class="section hidden" id="patientPreviewSection">
            <h2>步骤 6: 患者视频预览与评估</h2>
            <video id="patientVideoPreview" class="patient-video-preview" controls></video>
            <div class="controls">
                <button class="btn btn-success" id="assessBtn">🎯 开始自动评估</button>
                <span id="patientVideoInfo" style="color:#888;"></span>
            </div>
            <div id="assessStatus" class="status hidden"></div>
        </div>

        <!-- 步骤7: 评估结果 -->
        <div class="section hidden" id="resultsSection">
            <h2>步骤 7: 评估结果</h2>

            <!-- 总体评分 -->
            <div class="overall-summary" id="overallSummary">
                <h2>总体评分</h2>
                <div class="big-score" id="overallScore">--</div>
                <p id="overallGrade">评估中...</p>
            </div>

            <!-- 各动作详细评分 -->
            <h3 style="margin-bottom: 15px; color: #374151;">各动作详细评分</h3>
            <div class="results-grid" id="resultsGrid"></div>
        </div>
    </div>

    <!-- 帧预览窗口 -->
    <div id="framePreview" class="frame-preview hidden">
        <div class="frame-preview-loading" id="framePreviewLoading">
            加载中...
        </div>
        <img id="framePreviewImg" class="hidden" alt="Frame Preview">
        <div class="frame-preview-info" id="framePreviewInfo"></div>
    </div>

    <script>
        // 全局状态
        let videoId = null;
        let segments = [];
        let videoMetadata = {};
        let totalFrames = 0;
        let fps = 30;

        // 拖拽状态（全局）
        let dragState = {
            isDragging: false,
            dragHandle: null,
            segmentIdx: null,
            startX: 0,
            initialValue: 0
        };

        // 动作类型选项
        const actionTypes = ['squat', 'stand_up', 'leg_raise', 'arm_raise', 'arms_up', 'arms_down', 'other'];

        // 动作类型中文名称映射
        const actionTypeNames = {
            'squat': '深蹲',
            'stand_up': '站起',
            'leg_raise': '抬腿',
            'arm_raise': '举臂',
            'arms_up': '把手举到头顶',
            'arms_down': '把手放下到腿部',
            'other': '其他'
        };

        // 帧预览相关
        let framePreviewThrottle = null;
        let currentFetchController = null;
        const frameCache = new Map(); // frameNum -> {url, timestamp}
        const THROTTLE_DELAY = 150; // ms
        const CACHE_SIZE_LIMIT = 50; // 最多缓存50帧
        const PREVIEW_OFFSET_X = 20; // 预览框相对鼠标的X偏移
        const PREVIEW_OFFSET_Y = -200; // 预览框相对鼠标的Y偏移

        // DOM元素
        const uploadArea = document.getElementById('uploadArea');
        const videoFile = document.getElementById('videoFile');
        const videoPreview = document.getElementById('videoPreview');
        const segmentBtn = document.getElementById('segmentBtn');
        const saveBtn = document.getElementById('saveBtn');
        const resetBtn = document.getElementById('resetBtn');

        // 帧预览DOM元素
        const framePreviewEl = document.getElementById('framePreview');
        const framePreviewImg = document.getElementById('framePreviewImg');
        const framePreviewLoading = document.getElementById('framePreviewLoading');
        const framePreviewInfo = document.getElementById('framePreviewInfo');

        // 文件上传
        videoFile.addEventListener('change', handleFileSelect);

        // 显式处理label点击（确保跨浏览器兼容性）
        const fileLabel = document.querySelector('label[for="videoFile"]');
        fileLabel.addEventListener('click', function(e) {
            e.preventDefault(); // 阻止label默认行为
            e.stopPropagation();
            videoFile.click(); // 直接触发input点击
        });

        uploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            uploadArea.classList.add('dragover');
        });
        uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('dragover'));
        uploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            uploadArea.classList.remove('dragover');
            if (e.dataTransfer.files.length) {
                videoFile.files = e.dataTransfer.files;
                handleFileSelect();
            }
        });

        async function handleFileSelect() {
            const file = videoFile.files[0];
            if (!file) return;

            showStatus('uploadStatus', `正在上传 ${file.name}...`, 'info');

            const formData = new FormData();
            formData.append('video', file);

            try {
                const res = await fetch('/api/expert/upload', {
                    method: 'POST',
                    body: formData
                });
                const data = await res.json();

                if (data.success) {
                    videoId = data.video_id;
                    videoMetadata = data.metadata;

                    // 显示视频预览
                    const videoUrl = URL.createObjectURL(file);
                    videoPreview.src = videoUrl;

                    document.getElementById('videoInfo').textContent =
                        `${data.metadata.total_frames} 帧 | ${data.metadata.fps.toFixed(2)} FPS | ${data.metadata.duration.toFixed(1)}秒`;

                    showStatus('uploadStatus', `✓ 上传成功: ${file.name}`, 'success');
                    document.getElementById('previewSection').classList.remove('hidden');
                } else {
                    showStatus('uploadStatus', `✗ 上传失败: ${data.error}`, 'error');
                }
            } catch (error) {
                showStatus('uploadStatus', `✗ 上传错误: ${error.message}`, 'error');
            }
        }

        // 自动分割
        segmentBtn.addEventListener('click', async () => {
            showStatus('segmentStatus', '正在分析视频并自动分割...', 'info');
            segmentBtn.disabled = true;

            try {
                const res = await fetch('/api/expert/segment', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({video_id: videoId})
                });
                const data = await res.json();

                if (data.success) {
                    segments = data.segments;
                    totalFrames = data.total_frames;
                    fps = data.fps;

                    console.log('分割成功:', {
                        segments: segments,
                        totalFrames: totalFrames,
                        fps: fps
                    });

                    showStatus('segmentStatus', `✓ 检测到 ${segments.length} 个动作段`, 'success');

                    // 显示时间轴和标注区域
                    document.getElementById('timelineSection').classList.remove('hidden');
                    document.getElementById('labelSection').classList.remove('hidden');

                    renderTimeline();
                    renderSegmentsList();
                } else {
                    showStatus('segmentStatus', `✗ 分割失败: ${data.error}`, 'error');
                }
            } catch (error) {
                showStatus('segmentStatus', `✗ 分割错误: ${error.message}`, 'error');
            } finally {
                segmentBtn.disabled = false;
            }
        });

        // 渲染时间轴
        function renderTimeline() {
            const timeline = document.getElementById('timeline');
            timeline.innerHTML = '';

            console.log('开始渲染时间轴:', {
                segmentsCount: segments.length,
                totalFrames: totalFrames,
                fps: fps
            });

            if (!totalFrames || totalFrames === 0) {
                console.error('totalFrames无效:', totalFrames);
                return;
            }

            segments.forEach((seg, idx) => {
                const startPercent = (seg.start / totalFrames) * 100;
                const widthPercent = ((seg.end - seg.start) / totalFrames) * 100;

                console.log(`分段 ${idx}:`, {
                    start: seg.start,
                    end: seg.end,
                    startPercent: startPercent,
                    widthPercent: widthPercent
                });

                const segDiv = document.createElement('div');
                segDiv.className = `segment ${seg.action_type ? 'labeled' : ''}`;
                segDiv.style.left = `${startPercent}%`;
                segDiv.style.width = `${widthPercent}%`;
                segDiv.dataset.id = idx;

                segDiv.innerHTML = `
                    <div class="segment-label">${seg.action_type ? actionTypeNames[seg.action_type] : `段${idx+1}`}</div>
                    <div class="segment-time">${seg.start}-${seg.end} (${seg.duration}s)</div>
                    <div class="resize-handle left"></div>
                    <div class="resize-handle right"></div>
                `;

                // 添加拖拽调整边界的功能
                setupSegmentResize(segDiv, idx);

                timeline.appendChild(segDiv);
            });

            console.log('时间轴渲染完成，子元素数量:', timeline.children.length);

            // 移除旧的事件监听器（如果存在）
            timeline.removeEventListener('mousemove', handleTimelineHover);
            timeline.removeEventListener('mouseleave', handleTimelineLeave);

            // 添加时间轴悬停预览功能
            timeline.addEventListener('mousemove', handleTimelineHover);
            timeline.addEventListener('mouseleave', handleTimelineLeave);
        }

        // 处理时间轴悬停
        function handleTimelineHover(e) {
            // 如果正在拖拽，不处理悬停
            if (dragState.isDragging) return;
            if (!videoId) return;

            const timeline = document.getElementById('timeline');
            const timelineRect = timeline.getBoundingClientRect();

            // 计算鼠标位置对应的帧号
            const relativeX = e.clientX - timelineRect.left;
            const percentage = relativeX / timelineRect.width;
            const frameNum = Math.max(0, Math.min(totalFrames - 1, Math.round(percentage * totalFrames)));

            // 更新视频播放器进度
            const timeInSeconds = frameNum / fps;
            videoPreview.currentTime = timeInSeconds;
        }

        // 处理时间轴离开
        function handleTimelineLeave() {
            // 不需要任何操作，视频保持在当前位置
        }

        // 设置分段边界拖拽调整
        function setupSegmentResize(segDiv, segmentIdx) {
            const leftHandle = segDiv.querySelector('.resize-handle.left');
            const rightHandle = segDiv.querySelector('.resize-handle.right');

            console.log(`设置分段 ${segmentIdx} 的拖拽功能`);

            // 左边界拖拽
            leftHandle.addEventListener('mousedown', (e) => {
                console.log('开始拖拽左边界, 分段:', segmentIdx);
                dragState.isDragging = true;
                dragState.dragHandle = 'left';
                dragState.segmentIdx = segmentIdx;
                dragState.startX = e.clientX;
                dragState.initialValue = segments[segmentIdx].start;
                e.stopPropagation();
                e.preventDefault();
                document.body.style.cursor = 'ew-resize';
            });

            // 右边界拖拽
            rightHandle.addEventListener('mousedown', (e) => {
                console.log('开始拖拽右边界, 分段:', segmentIdx);
                dragState.isDragging = true;
                dragState.dragHandle = 'right';
                dragState.segmentIdx = segmentIdx;
                dragState.startX = e.clientX;
                dragState.initialValue = segments[segmentIdx].end;
                e.stopPropagation();
                e.preventDefault();
                document.body.style.cursor = 'ew-resize';
            });
        }

        // ============ 帧预览功能函数 ============

        /**
         * 显示帧预览（带节流）
         */
        function showFramePreview(frameNum, mouseX, mouseY) {
            if (framePreviewThrottle) {
                clearTimeout(framePreviewThrottle);
            }

            framePreviewThrottle = setTimeout(() => {
                fetchAndDisplayFrame(frameNum, mouseX, mouseY);
            }, THROTTLE_DELAY);
        }

        /**
         * 获取并显示帧
         */
        async function fetchAndDisplayFrame(frameNum, mouseX, mouseY) {
            // 显示预览框
            framePreviewEl.classList.remove('hidden', 'error');

            // 定位预览框（避免超出视口）
            let left = mouseX + PREVIEW_OFFSET_X;
            let top = mouseY + PREVIEW_OFFSET_Y;

            const previewWidth = 240;
            const previewHeight = 180;
            const viewportWidth = window.innerWidth;
            const viewportHeight = window.innerHeight;

            // 边界检查
            if (left + previewWidth > viewportWidth) {
                left = mouseX - previewWidth - PREVIEW_OFFSET_X;
            }
            if (top < 0) {
                top = mouseY + 20;
            }
            if (top + previewHeight > viewportHeight) {
                top = viewportHeight - previewHeight - 10;
            }

            framePreviewEl.style.left = `${left}px`;
            framePreviewEl.style.top = `${top}px`;

            // 更新信息
            const timeSeconds = (frameNum / fps).toFixed(2);
            framePreviewInfo.textContent = `帧 ${frameNum} / ${totalFrames} (${timeSeconds}s)`;

            // 检查缓存
            if (frameCache.has(frameNum)) {
                const cached = frameCache.get(frameNum);
                displayFrameImage(cached.url);
                return;
            }

            // 显示加载状态
            framePreviewLoading.classList.remove('hidden');
            framePreviewImg.classList.add('hidden');

            // 取消之前的请求
            if (currentFetchController) {
                currentFetchController.abort();
            }

            currentFetchController = new AbortController();

            try {
                const response = await fetch(
                    `/api/expert/video/${videoId}/frame/${frameNum}?thumbnail=true`,
                    { signal: currentFetchController.signal }
                );

                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}`);
                }

                const blob = await response.blob();
                const imageUrl = URL.createObjectURL(blob);

                // 添加到缓存
                addToCache(frameNum, imageUrl);

                // 显示图像
                displayFrameImage(imageUrl);

            } catch (error) {
                if (error.name === 'AbortError') {
                    console.log('Frame fetch aborted');
                    return;
                }

                console.error('Error fetching frame:', error);
                framePreviewEl.classList.add('error');
                framePreviewLoading.textContent = '加载失败';
            }
        }

        /**
         * 显示帧图像
         */
        function displayFrameImage(imageUrl) {
            framePreviewImg.src = imageUrl;
            framePreviewImg.classList.remove('hidden');
            framePreviewLoading.classList.add('hidden');
        }

        /**
         * 添加到缓存（带LRU清理）
         */
        function addToCache(frameNum, imageUrl) {
            // 如果缓存已满，移除最旧的
            if (frameCache.size >= CACHE_SIZE_LIMIT) {
                const oldestKey = frameCache.keys().next().value;
                const oldestEntry = frameCache.get(oldestKey);
                URL.revokeObjectURL(oldestEntry.url); // 释放内存
                frameCache.delete(oldestKey);
            }

            frameCache.set(frameNum, {
                url: imageUrl,
                timestamp: Date.now()
            });
        }

        /**
         * 隐藏帧预览
         */
        function hideFramePreview() {
            framePreviewEl.classList.add('hidden');

            // 清除节流定时器
            if (framePreviewThrottle) {
                clearTimeout(framePreviewThrottle);
                framePreviewThrottle = null;
            }

            // 取消正在进行的请求
            if (currentFetchController) {
                currentFetchController.abort();
                currentFetchController = null;
            }
        }

        /**
         * 清理缓存
         */
        function clearFrameCache() {
            frameCache.forEach(entry => {
                URL.revokeObjectURL(entry.url);
            });
            frameCache.clear();
        }

        // ============ 全局拖拽事件处理（页面加载时添加一次） ============
        document.addEventListener('mousemove', (e) => {
            if (!dragState.isDragging || dragState.segmentIdx === null) return;

            const timeline = document.getElementById('timeline');
            const timelineRect = timeline.getBoundingClientRect();
            const deltaX = e.clientX - dragState.startX;
            const deltaFrames = Math.round((deltaX / timelineRect.width) * totalFrames);

            const idx = dragState.segmentIdx;
            let currentFrame = null; // 当前帧号

            if (dragState.dragHandle === 'left') {
                // 调整起始帧
                const newStart = Math.max(0, dragState.initialValue + deltaFrames);
                const minLength = Math.round(fps * 0.5);
                const minEnd = newStart + minLength;

                if (newStart < segments[idx].end && segments[idx].end >= minEnd) {
                    segments[idx].start = newStart;
                    segments[idx].duration = ((segments[idx].end - newStart) / fps).toFixed(2);
                    currentFrame = newStart; // 记录当前帧
                    renderTimeline();
                    renderSegmentsList();
                }
            } else if (dragState.dragHandle === 'right') {
                // 调整结束帧
                const newEnd = Math.min(totalFrames, dragState.initialValue + deltaFrames);
                const minLength = Math.round(fps * 0.5);
                const minEnd = segments[idx].start + minLength;

                if (newEnd > segments[idx].start && newEnd >= minEnd) {
                    segments[idx].end = newEnd;
                    segments[idx].duration = ((newEnd - segments[idx].start) / fps).toFixed(2);
                    currentFrame = newEnd; // 记录当前帧
                    renderTimeline();
                    renderSegmentsList();
                }
            }

            // 拖动时更新视频播放器进度（不显示帧预览）
            if (currentFrame !== null) {
                // 将帧号转换为时间（秒）
                const timeInSeconds = currentFrame / fps;
                videoPreview.currentTime = timeInSeconds;
            }
        });

        document.addEventListener('mouseup', () => {
            if (dragState.isDragging) {
                console.log('结束拖拽');
                dragState.isDragging = false;
                dragState.dragHandle = null;
                dragState.segmentIdx = null;
                document.body.style.cursor = 'default';
            }
        });

        // 渲染分段列表
        function renderSegmentsList() {
            const list = document.getElementById('segmentsList');
            list.innerHTML = '';

            segments.forEach((seg, idx) => {
                const card = document.createElement('div');
                card.className = `segment-card ${seg.action_type ? 'labeled' : ''}`;
                card.innerHTML = `
                    <h3>动作段 ${idx + 1}</h3>
                    <label>
                        动作类型:
                        <select data-id="${idx}">
                            <option value="">-- 请选择 --</option>
                            ${actionTypes.map(type =>
                                `<option value="${type}" ${seg.action_type === type ? 'selected' : ''}>${actionTypeNames[type]}</option>`
                            ).join('')}
                        </select>
                    </label>
                    <div class="info">
                        帧范围: ${seg.start} - ${seg.end}<br>
                        时长: ${seg.duration} 秒
                    </div>
                `;

                card.querySelector('select').addEventListener('change', (e) => {
                    segments[idx].action_type = e.target.value;
                    renderTimeline();
                    card.classList.toggle('labeled', !!e.target.value);
                });

                list.appendChild(card);
            });
        }

        // 保存标注
        saveBtn.addEventListener('click', async () => {
            const labeled = segments.filter(s => s.action_type);

            if (labeled.length === 0) {
                showStatus('saveStatus', '⚠️ 请至少标注一个分段', 'error');
                return;
            }

            showStatus('saveStatus', `正在生成 ${labeled.length} 个模板...`, 'info');
            saveBtn.disabled = true;

            try {
                const res = await fetch('/api/expert/label', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        video_id: videoId,
                        segments: segments
                    })
                });
                const data = await res.json();

                if (data.success) {
                    showStatus('saveStatus',
                        `✓ ${data.message}\n创建的模板: ${data.templates.join(', ')}`,
                        'success');
                } else {
                    showStatus('saveStatus', `✗ 保存失败: ${data.error}`, 'error');
                }
            } catch (error) {
                showStatus('saveStatus', `✗ 保存错误: ${error.message}`, 'error');
            } finally {
                saveBtn.disabled = false;
            }
        });

        // 重置
        resetBtn.addEventListener('click', () => {
            if (confirm('确定要重新开始吗？')) {
                location.reload();
            }
        });

        // ========== 患者视频评估相关代码 ==========

        // 患者视频相关全局变量
        let patientVideoId = null;
        let patientAssessmentResults = null;

        // 患者视频相关DOM元素
        const patientUploadArea = document.getElementById('patientUploadArea');
        const patientVideoFile = document.getElementById('patientVideoFile');
        const patientVideoPreview = document.getElementById('patientVideoPreview');
        const assessBtn = document.getElementById('assessBtn');

        // 患者视频文件上传
        patientVideoFile.addEventListener('change', handlePatientFileSelect);

        // 患者视频label点击处理
        const patientFileLabel = document.querySelector('label[for="patientVideoFile"]');
        patientFileLabel.addEventListener('click', function(e) {
            e.preventDefault();
            e.stopPropagation();
            patientVideoFile.click();
        });

        // 患者视频拖放处理
        patientUploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            patientUploadArea.classList.add('dragover');
        });
        patientUploadArea.addEventListener('dragleave', () => patientUploadArea.classList.remove('dragover'));
        patientUploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            patientUploadArea.classList.remove('dragover');
            if (e.dataTransfer.files.length) {
                patientVideoFile.files = e.dataTransfer.files;
                handlePatientFileSelect();
            }
        });

        // 处理患者视频文件选择
        async function handlePatientFileSelect() {
            const file = patientVideoFile.files[0];
            if (!file) return;

            showStatus('patientUploadStatus', '正在上传患者视频...', 'info');

            const formData = new FormData();
            formData.append('video', file);

            try {
                const res = await fetch('/api/patient/upload', {
                    method: 'POST',
                    body: formData
                });
                const data = await res.json();

                if (data.success) {
                    patientVideoId = data.video_id;
                    patientVideoPreview.src = `/api/patient/video/${patientVideoId}`;

                    showStatus('patientUploadStatus', `✓ 患者视频上传成功！`, 'success');
                    document.getElementById('patientPreviewSection').classList.remove('hidden');
                    document.getElementById('patientVideoInfo').textContent =
                        `视频时长: ${data.duration.toFixed(1)}秒 | 帧率: ${data.fps} fps | 总帧数: ${data.total_frames}`;
                } else {
                    showStatus('patientUploadStatus', `✗ 上传失败: ${data.error}`, 'error');
                }
            } catch (error) {
                showStatus('patientUploadStatus', `✗ 上传错误: ${error.message}`, 'error');
            }
        }

        // 开始评估
        assessBtn.addEventListener('click', async () => {
            if (!patientVideoId) {
                alert('请先上传患者视频');
                return;
            }

            showStatus('assessStatus', '正在进行自动评估，请稍候...', 'info');
            assessBtn.disabled = true;

            try {
                const res = await fetch('/api/patient/assess', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({
                        video_id: patientVideoId
                    })
                });
                const data = await res.json();

                if (data.success) {
                    patientAssessmentResults = data.results;
                    showStatus('assessStatus', `✓ 评估完成！检测到 ${data.results.segments.length} 个动作`, 'success');

                    // 显示结果
                    displayAssessmentResults(data.results);
                    document.getElementById('resultsSection').classList.remove('hidden');

                    // 滚动到结果区域
                    document.getElementById('resultsSection').scrollIntoView({ behavior: 'smooth' });
                } else {
                    showStatus('assessStatus', `✗ 评估失败: ${data.error}`, 'error');
                }
            } catch (error) {
                showStatus('assessStatus', `✗ 评估错误: ${error.message}`, 'error');
            } finally {
                assessBtn.disabled = false;
            }
        });

        // 显示评估结果
        function displayAssessmentResults(results) {
            // 显示总体评分
            const overallScore = results.overall_score;
            document.getElementById('overallScore').textContent = overallScore.toFixed(1);

            let grade = '';
            let gradeClass = '';
            if (overallScore >= 90) {
                grade = '优秀 (Excellent)';
                gradeClass = 'excellent';
            } else if (overallScore >= 75) {
                grade = '良好 (Good)';
                gradeClass = 'good';
            } else if (overallScore >= 60) {
                grade = '及格 (Fair)';
                gradeClass = 'fair';
            } else {
                grade = '需要改进 (Poor)';
                gradeClass = 'poor';
            }
            document.getElementById('overallGrade').textContent = grade;
            document.getElementById('overallSummary').style.background =
                gradeClass === 'excellent' ? 'linear-gradient(135deg, #10b981 0%, #059669 100%)' :
                gradeClass === 'good' ? 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)' :
                gradeClass === 'fair' ? 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)' :
                'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)';

            // 显示各动作详细评分
            const resultsGrid = document.getElementById('resultsGrid');
            resultsGrid.innerHTML = '';

            results.segments.forEach((segment, idx) => {
                const score = segment.score;
                const scoreClass = score >= 90 ? 'excellent' : score >= 75 ? 'good' : score >= 60 ? 'fair' : 'poor';

                const card = document.createElement('div');
                card.className = `result-card ${scoreClass}`;
                card.innerHTML = `
                    <div class="result-header">
                        <h3>动作 ${idx + 1}: ${actionTypeNames[segment.action_type] || '未知动作'}</h3>
                        <div class="score-badge ${scoreClass}">${score.toFixed(1)}</div>
                    </div>
                    <div class="result-details">
                        <div>
                            <span>时间范围:</span>
                            <strong>${(segment.start_frame / results.fps).toFixed(2)}s - ${(segment.end_frame / results.fps).toFixed(2)}s</strong>
                        </div>
                        <div>
                            <span>帧数范围:</span>
                            <strong>第 ${segment.start_frame} - ${segment.end_frame} 帧</strong>
                        </div>
                        <div style="margin-top: 10px;">
                            <span>相似度得分:</span>
                            <strong>${(segment.metrics.similarity * 100).toFixed(1)}%</strong>
                        </div>
                        <div class="metric-bar">
                            <div class="metric-bar-fill ${scoreClass}" style="width: ${segment.metrics.similarity * 100}%"></div>
                        </div>
                        <div style="margin-top: 5px;">
                            <span>几何得分:</span>
                            <strong>${(segment.metrics.geometric * 100).toFixed(1)}%</strong>
                        </div>
                        <div class="metric-bar">
                            <div class="metric-bar-fill ${scoreClass}" style="width: ${segment.metrics.geometric * 100}%"></div>
                        </div>
                        <div style="margin-top: 5px;">
                            <span>属性得分:</span>
                            <strong>${(segment.metrics.attribute * 100).toFixed(1)}%</strong>
                        </div>
                        <div class="metric-bar">
                            <div class="metric-bar-fill ${scoreClass}" style="width: ${segment.metrics.attribute * 100}%"></div>
                        </div>
                        ${segment.matched_template ? `
                            <div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #e5e7eb;">
                                <span>匹配模板:</span>
                                <strong>${segment.matched_template}</strong>
                            </div>
                        ` : ''}
                    </div>
                `;
                resultsGrid.appendChild(card);
            });
        }

        // ========== 患者视频评估代码结束 ==========

        // 工具函数
        function showStatus(elementId, message, type) {
            const statusEl = document.getElementById(elementId);
            statusEl.textContent = message;
            statusEl.className = `status ${type === 'error' ? 'error' : type === 'success' ? 'success' : ''}`;
            statusEl.classList.remove('hidden');
        }

        // 页面卸载时清理缓存
        window.addEventListener('beforeunload', () => {
            clearFrameCache();
        });
    </script>
</body>
</html>
